package de.slothsoft.commons.swing.window;

import java.awt.Window;

import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import de.slothsoft.commons.IDisplayable;
import de.slothsoft.commons.swing.JCheckList;
import de.slothsoft.commons.swing.databinding.DataBinding;
import de.slothsoft.commons.swing.util.DataBindingUtil;
import de.slothsoft.commons.swing.validation.IValidationListener;
import de.slothsoft.commons.swing.validation.IValidationListener.ValidationEvent;
import de.slothsoft.commons.swing.validation.ValidationBinding;

public abstract class AbstractDataBindingDialog<T> extends AbstractDialog {

	private static final long serialVersionUID = 1L;

	protected static <S> S openDialog(AbstractDataBindingDialog<S> dialog) {
		dialog.open();
		if (dialog.getAnswer() == ButtonType.OK)
			return dialog.getModel();
		return null;
	}

	/*
	 * 
	 */

	private ValidationBinding validationBinding;
	private DataBinding<T> dataBinding;
	private T model;

	public AbstractDataBindingDialog(Window parent, T model) {
		super(parent);
		this.model = model;
	}

	public AbstractDataBindingDialog(Window parent, T model,
			ButtonType... buttons) {
		super(parent, buttons);
		this.model = model;
	}

	@Override
	protected final void createMainPanel(JPanel mainPanel) {
		this.validationBinding = new ValidationBinding();
		this.validationBinding.addListener(new IValidationListener() {

			@Override
			public void validationChanged(ValidationEvent event) {
				performValidationChanged(event);
			}
		});
		this.dataBinding = new DataBinding<T>(this.model);
		createMainPanel(mainPanel, this.dataBinding, this.validationBinding);
		this.dataBinding.updateTargets();
	}

	protected void performValidationChanged(ValidationEvent event) {
		JButton button = getButton(ButtonType.OK);
		if (button != null) {
			button.setEnabled(event.getNewValue());
		}
	}

	@Override
	protected void createLayout() {
		super.createLayout();
		// we need the ok button to be there
		this.validationBinding.validate();
	}

	@Override
	protected void performOkAction() {
		getDataBinding().updateModels();
		performOkAction(getModel());
		super.performOkAction();
		close();
	}

	protected abstract void performOkAction(T model);

	protected abstract void createMainPanel(JPanel mainPanel,
			DataBinding<T> dataBinding, ValidationBinding validationBinding);

	public DataBinding<T> getDataBinding() {
		return this.dataBinding;
	}

	public ValidationBinding getValidationBinding() {
		return this.validationBinding;
	}

	public T getModel() {
		return this.model;
	}

	protected JTextField createText(JComponent component, String property,
			int x, int y) {
		JTextField textField = super.createText(component, x, y);
		this.dataBinding.bind(textField, this.model, property);
		return textField;
	}

	protected JTextField createText(JPanel parent, String labelName,
			String property, boolean mandatory, int x, int y) {
		return createText(parent, labelName, property, mandatory, null, x, y);
	}

	@SuppressWarnings("unchecked")
	protected JTextField createText(JPanel parent, String labelName,
			String property, boolean mandatory, Class<?> returnType, int x,
			int y) {
		createLabel(parent, labelName, x, y);
		JTextField text = DataBindingUtil.createText(getDataBinding(), parent,
				property, x + 1, y);
		getValidationBinding().addValidator(text, labelName, mandatory,
				returnType);
		return text;
	}

	protected JTextArea createTextArea(JComponent component, String property,
			int x, int y) {
		JTextArea textArea = super.createTextArea(component, x, y);
		textArea.setWrapStyleWord(true);
		textArea.setLineWrap(true);
		this.dataBinding.bind(textArea, this.model, property);
		return textArea;
	}

	@SuppressWarnings("unchecked")
	protected JTextArea createTextArea(JPanel parent, String labelName,
			String property, boolean mandatory, int x, int y) {
		createLabel(parent, labelName, x, y);
		JTextArea text = DataBindingUtil.createTextArea(getDataBinding(),
				parent, property, x + 1, y);
		text.setFont(getFont());
		if (mandatory) {
			getValidationBinding().addValidator(text, labelName, true);
		}
		return text;
	}

	protected JCheckBox createCheckBox(JComponent component, String property,
			int x, int y) {
		JCheckBox check = super.createCheckBox(component, x, y);
		this.dataBinding.bind(check, this.model, property);
		return check;
	}

	protected <S> JComboBox<S> createCombo(JComponent parent, String property,
			ComboBoxModel<S> model, int x, int y) {
		JComboBox<S> combo = super.createCombo(parent, model, x, y);
		this.dataBinding.bind(combo, this.model, property);
		return combo;
	}

	@SuppressWarnings("unchecked")
	protected <S> JComboBox<S> createCombo(JPanel parent, String labelName,
			String property, ComboBoxModel<S> model, boolean mandatory, int x,
			int y) {
		createLabel(parent, labelName, x, y);
		JComboBox<S> combo = DataBindingUtil.createCombo(getDataBinding(),
				parent, property, model, x + 1, y);
		combo.setFont(getFont());
		if (mandatory) {
			getValidationBinding().addValidator(combo, labelName, true);
		}
		return combo;
	}

	protected <U extends IDisplayable> JCheckList<U> createCheckList(
			JComponent parent, String property, U[] elements, int x, int y) {
		JCheckList<U> checkList = super.createCheckList(parent, elements, x, y);
		this.dataBinding.bind(checkList, this.model, property);
		return checkList;
	}

}
